/*
 * ethernet.cpp
 *
 *  Created on: May 25, 2018
 *      Author: yuantao
 */
#include "stdafx.h"

#include "ethernet.h"

#ifdef _WIN32
#include "windows.h"
#else
#include <unistd.h>
#include <pthread.h>
#endif

#include <fcntl.h>
#include <errno.h>
#include <string.h>

#include "log_ctl.h"

using namespace std;


CEthernet::CEthernet()
{
	m_sockRemote = -1;
	m_sockLocal = -1;
    memset(&m_stConnConfig,0,sizeof(connection_config));
    pfuncRecvCallBack = NULL;
}

CEthernet::~CEthernet()
{
	stopConnection();
}

void CEthernet::setConnectionConfig(connection_config * pstConnConfig)
{
    memcpy(&m_stConnConfig,pstConnConfig,sizeof(connection_config));
}

int CEthernet::startConnection(int isClient,int ip_protocol)
{
	struct sockaddr_in zeroIp;
	memset(&zeroIp, 0, sizeof(struct sockaddr_in));
    int flags = 0;

	short p_proto = IPPROTO_TCP;
	int type;// Ì×œÓ×ÖÃèÊö·û

	if (ip_protocol == PROTOCOL_UDP)
	{
		p_proto = IPPROTO_UDP;
		type = SOCK_DGRAM;
	}
	else
	{
		p_proto = IPPROTO_TCP;
		type = SOCK_STREAM;
	}


	m_sockLocal = socket(PF_INET, type, p_proto);
	if (-1 == m_sockLocal)
	{
		return 2;
	}

	//set socket to none-blocking

#ifdef _WIN32
	unsigned long ul = 1;
	ioctlsocket(m_sockLocal, FIONBIO, (unsigned long *)&ul);
#else
	flags = fcntl(m_sockLocal, F_GETFL, 0);
	fcntl(m_sockLocal, F_SETFL, flags | O_NONBLOCK);
#endif

	if (ip_protocol == PROTOCOL_UDP)
	{
		//UDP communication need no connection
		sockaddr_in RecvAddr;

		RecvAddr.sin_family = AF_INET;
		RecvAddr.sin_port = m_stConnConfig.localAddr.sin_port;
		RecvAddr.sin_addr.s_addr = m_stConnConfig.localAddr.sin_addr.s_addr;

		bind(m_sockLocal, (struct sockaddr *)&RecvAddr, sizeof(RecvAddr));

		m_stConnConfig.remoteAddr.sin_family = AF_INET;

		m_isClient = 1;
		m_ip_protocol = PROTOCOL_UDP;

#ifdef _WIN32
		readUdpData_ThreadHandle = CreateThread(NULL, 0, threadFunc_recvUdpData, this, 0, NULL);
#else
		int retCreate = pthread_create(&readUdpData_ThreadHandle, NULL, threadFunc_recvUdpData, this);
		if (retCreate == 0)
		{
			cout << "start read udp data thread success\n" << endl;
		}
		else
		{
			cout << "failed to start read udp data thread,error:\n" << retCreate;
		}
#endif



		return NO_ERROR;
	}

	int compRet = memcmp(&m_stConnConfig.remoteAddr, &zeroIp, sizeof(struct sockaddr_in));

	if (compRet == 0)
	{
		return 1;
	}

	if(isClient == 1)
	{
		//work as client, for tcp communication, connect to server
		int ret = connect(m_sockLocal, (struct sockaddr*)&m_stConnConfig.remoteAddr, sizeof(struct sockaddr_in));
		if (ret == -1)
		{
			//not completed due to nonblocking
			if (errno == EINPROGRESS)
			{
				fd_set writefds;
				fd_set readfds;
				fd_set exceptfds;
				FD_ZERO(&readfds);
				FD_ZERO(&exceptfds);
				FD_ZERO(&writefds);
				FD_SET(m_sockLocal, &writefds);
				struct timeval timeoutVal;
				timeoutVal.tv_usec = 3000;
				timeoutVal.tv_sec = 5;
				int selRet = select(
					1,
					&readfds,
					&writefds,
					&exceptfds,
					&timeoutVal
					);

				if (-1 == selRet)
				{
					return 4;
				}

				if (0 == selRet)
				{
					return 5;
				}

				if (selRet == 1)
				{
					m_isClient = 1;
					m_ip_protocol = PROTOCOL_TCP;
					return NO_ERROR;
				}
			}
			else
			{
				//listen sock corrupt
				return 1;
			}
			return 3;
		}
	}
	return NO_ERROR;
}

int CEthernet::stopConnection(void)
{
	if(m_sockLocal != -1)
	{
#ifdef _WIN32
		closesocket(m_sockLocal);
#else
		close(m_sockLocal);
#endif
	
       m_sockLocal = -1;
	}

	if(m_sockRemote != -1)
	{
#ifdef _WIN32
		closesocket(m_sockRemote);
#else
		close(m_sockRemote);
#endif		
        m_sockRemote = -1;
	}

	memset(&m_stConnConfig,0,sizeof(connection_config));

	recvThreadRunningFlag = STOPPING;

	while (recvThreadRunningFlag != STOP_FINISHED)
	{
		Sleep(1);
	}

	return 0;
}

int CEthernet::recvUdpData(char * buffOut,unsigned int maxLengthIn)
{
	int ret = 0;
    struct sockaddr_in peer_addr;
    socklen_t peer_addr_len = sizeof(struct sockaddr_in);
    memset(&peer_addr,0,peer_addr_len);

	fd_set fdread;
	FD_ZERO(&fdread);
	FD_SET(m_sockLocal, &fdread);

	timeval tv;
	tv.tv_sec = 0;
	tv.tv_usec = 5000;

	int retSelect = select(0, &fdread, NULL, NULL, &tv);

	if (retSelect == 1)
	{
		if (FD_ISSET(m_sockLocal, &fdread))
		{
			ret = recvfrom(m_sockLocal, buffOut, maxLengthIn, 0,
				(struct sockaddr *) &peer_addr, &peer_addr_len);
		}
	}   

    return ret;
}

#ifdef _WIN32
DWORD WINAPI   threadFunc_recvUdpData(LPVOID arg)
#else
void *threadFunc_recvUdpData(void *arg)
#endif
{
	CEthernet* pUdpConnection = (CEthernet*)arg;
	pUdpConnection->recvThreadRunningFlag = RUNNING;
	char * recvBuff = new char[MAX_BYTES_RECV_ONCE];
	int sizeRecv = MAX_BYTES_RECV_ONCE;
    memset(recvBuff,0,MAX_BYTES_RECV_ONCE);
    static int count = 0;
	std::cout << "enter in threadFunc_recvUdpData" << std::endl;
    while(pUdpConnection->recvThreadRunningFlag == RUNNING)
    {
#ifdef _WIN32
		Sleep(0);
#else
		usleep(0);
#endif
        count++;
        if(count>=5000)
        {
           //cout<<"recv thread is running"<<endl;
           count = 0;
        }
    	
    	sizeRecv = pUdpConnection->recvUdpData(recvBuff,MAX_BYTES_RECV_ONCE);
    	if(sizeRecv > 0)
    	{
           //cout<<"recv data, length:"<<sizeRecv<<endl;
           //sendto(m_sockLocal,"from test host",15,(struct sockaddr *)&(m_stConnConfig.remoteAddr),sizeof(struct sockaddr));
           pUdpConnection->sendUdpData("from test host",15);
           pUdpConnection->sendUdpData(recvBuff,sizeRecv);

		   g_logCtl->info("recv from udp, %s\r\n", recvBuff);
           
           if(pUdpConnection->pfuncRecvCallBack != NULL)
           {
              (*pUdpConnection->pfuncRecvCallBack)(recvBuff,sizeRecv);
           }
    	}
        
    	memset(recvBuff,0,MAX_BYTES_RECV_ONCE);
    }

    delete [] recvBuff;
    recvBuff = NULL;

    pUdpConnection->recvThreadRunningFlag = STOP_FINISHED;

	cout << "recv udp data thread exit" << endl;
#ifdef _WIN32
	return 0;
#else
	pthread_exit((void*)&(pUdpConnection->recvThreadExitVal));
	return &(pUdpConnection->recvThreadExitVal);
#endif
}

void CEthernet::joinRecvThread(void)
{
	int *ret_join = NULL;
#ifdef _WIN32
	WaitForSingleObject(readUdpData_ThreadHandle,5000);
#else
	pthread_join(readUdpData_ThreadHandle, (void**)&ret_join);
#endif
    
}

int CEthernet::sendUdpData(const char * buffIn,unsigned int buffLen)
{
    int sendRet = sendto(m_sockLocal,buffIn,buffLen,0,(struct sockaddr *)&(m_stConnConfig.remoteAddr),sizeof(struct sockaddr));
    
	if (sendRet < 0)
	{
		int err_code = WSAGetLastError();

		loggerInfo("send udp data failed, errcode:%d\r\n", err_code);
	}
	//else
	//{
	//	loggerInfo("send udp data %d bytes success\r\n", sendRet);
	//}

	return sendRet;
}

void CEthernet::setRecvCallBackFunc(recvCallBack funcRecvCallBack)
{
    pfuncRecvCallBack = funcRecvCallBack;
}

